Java™ Development Kit (JDK) is a software development kit for producing Java™ programming language (referred to as “JPL” herein) programs. JDK version 1.5 introduced several extensions to the JPL. One of these extensions is the introduction of “generic types.”
Generic types in JPL are analogous to, but not exactly the same as, templates in the C++ programming language. Generic types may be most easily understood through a discussion of the scenarios that make the use of generic types beneficial.
According to the syntactical rules of strongly typed programming languages such as JPL, the data type of a variable is supposed to be expressed in the declaration of that variable. The following declaration is illustrative:
Integer x=new Integer(0);
In the above declaration, the variable “x” is declared to be of type “Integer.” Thus, any attempt to assign, to “x,” an object that not an instance of the “Integer” class or a subclass thereof, should be forbidden and should cause the compiler to alert the programmer that something is amiss in the assignment.
The following declaration is somewhat more sophisticated and informative:
List myintList=new LinkedList( );
In the above declaration, the variable “myIntList” is declared to be of type “List.” When the above expression is evaluated, a new instance of the “LinkedList” class (i.e., a “LinkedList” object) is created (“instantiated”) via the invocation of the “LinkedList( )” constructor method assigned to “myIntList.” The “List” and “LinkedList” classes are used as examples in the discussion below, but it should be understood that the behaviors of the classes discussed below are not limited only to these classes.
Like other variables of type “List,” the variable “mylntList” can be assigned “LinkedList” objects. The “LinkedList” object is a linked list of other objects. Prior to the introduction of generic types in JPL, the compiler could not determine what the specific types of these other objects were. The type system was a conservative approximation of run-time behavior. For example, without generic types, if only “Foo”-type objects were “inserted” into a LinkedList, then the compiler could not determine, upon “extraction” of these objects from the LinkedList, that the objects were of type “Foo.” Without generic types, the compiler merely could determine that these objects were instances of some indeterminable subtype of the “Object” class; in JPL, all classes are subclasses of the “Object” class. Because this was as specific as the compiler could get, the “extracted” objects could only have the methods of the “Object” class (as opposed to the “Foo” class) invoked relative to them.
Because of this shortcoming, it often became necessary for a programmer to “cast” such a returned object into a particular data type prior to assigning that object to a variable that was declared to be of the particular data type. The following code is demonstrative:
Integer x=(Integer) mylntList.iterator( ).next( );
In the above code, the “Object” type object returned by the method is expressly cast into an instance of class “Integer.” If this cast were not performed, then the compiler might complain that a non-“Integer” type object was being assigned to a variable of type “Integer.” Indeed, such a complaint would be beneficial, because if the compiler did not complain about such an assignment, then the compiled program might exhibit unexpected and unwanted behavior if and when the method returned an object that actually was not an Integer.
Having to cast objects in this manner usually irritates programmers and makes source code undesirably verbose and less readable. Some might even consider casting to be a “dirty” programming practice. Fortunately, the advent of generic types in JPL made casting unnecessary, at least for the purposes discussed above. A sample declaration that incorporates generic types follows:
List<Integer>myIntList=new LinkedList<Integer>( );
This is similar to the non-generically typed declaration above, except that the declaration expressly indicates that the objects within the linked list assigned to “myIntList” are going to be instances of class “Integer.” In light of the declaration, the compiler knows that every object returned by the “mylntList.iterator( ).next( )” method is an “Integer” object. The compiler will not complain about the expression
Integer x=myIntList.iterator( ).next( );
because the compiler can determine that the object returned by the method must be an “Integer” object, and that variable “x” can reference objects of type “Integer.” A single declaration makes potentially many casts unnecessary.
It is also should be noted that generic types allow for specificity in this manner without requiring multiple separate implementations of the class. Although “myIntList” is a “List” object that references “Integer” objects, it was not necessary for the programmer to specifically code an implementation of the “List” class to reference only “Integer” objects. Generic types allow a class to be defined once in a generic manner, and then invoked multiple times in different specific declarations.
Due to the more precise approximation of run-time behavior that generic types provide, generic types also help to avoid runtime failures.
An actual example of generic type declarations is shown below:
public interface List<E> {                void add(E x);        Iterator<E>iterator( );        
}
public interface Iterator<E> {                E next( );        boolean hasNext( );        
}
In the above generic type declarations, the angle bracket-enclosed parameters (e.g., “<E>”) are called the “formal type parameters” of the generic types. The specific invocation of such a generic type is called a “parameterized type.” For example, the following expression is a parameterized type:
List<Integer>
In the foregoing parameterized type, “<Integer>” is an example of an “actual type argument.” Parameterized types specify actual type arguments that are to be put in the place of formal type parameters when the generic type declaration is invoked. Although examples provided below contain only one formal type parameters and only one actual type argument, it should be understood that generic type declarations may specify multiple formal type parameters, and parameterized types may specify multiple actual type arguments.
Although JPL generic types and C++ templates are somewhat similar syntactically and functionally, there are some significant differences between the two. The declaration of a JPL generic type is not expanded, neither within source code nor within binary code, into multiple different specific declarations for different actual type arguments. Instead, much like with non-generic type declarations in JPL, a JPL generic type declaration is compiled into a single class file.
Because generic types are so useful, many of the non-generic type declarations that used to exist in the JPL libraries have been replaced with generic type declarations. Non-generic method declarations also have been replaced in these libraries with generic method declarations. In other words, the former non-generic type and method declarations have been “generified.” The existing libraries have been generified in a way that allows migration compatibility. Programmers who are writing new JPL programs can make use of the generic types and methods by putting parameterized types in their programs.
It is often useful for a program to have a way of determining, at run time, characteristics of objects that have been instantiated. The ability to determine such characteristics is called “introspection.” The JPL Core Reflection Application Programming Interface (API) supports introspection about the classes and objects in the current JPL Virtual Machine (JVM). The JPL Core Reflection API can be used to construct new class instances and new arrays, access and modify fields of objects and classes, invoke methods on objects and classes, and access and modify elements of arrays. The mechanisms of the JPL Core Reflection API allow certain program elements (e.g., classes, fields, methods, etc.) to have a corresponding “reflective proxy” that indicates information about those program elements.
Among the methods provided by the JPL Core Reflection API is the “toString( )” method. The “toString( )” method returns a string representation of a reflective proxy object relative to which the method was invoked. For example, the following code returns a string representation of the “f” field of a reflexive proxy corresponding to the class of object “myObject”:
myObject.getClass( ).getField(“f”).toString( );
All classes inherit the “toString( )” method from the Object class. However, a class may override the “toString( )” method to provide a specialized implementation of the method that produces information that is meaningful to that class in particular.
The “toString( )” method existed before the introduction of generic types to the JPL. As a result, many existing JPL programs use the “toString( )” method, but are not “aware” of generic types. The behavior of these existing programs is sometimes dependent upon the results produced by the “toString( )” method. If the “toString( )” method were modified to produce information that differed from the information expected by an existing program, then it might cause the existing program to malfunction.
For example, an older program might contain ungenerified code such as follows:
Cc=new C( );
c.getClass( ).getField(“f”).toString( );
Meanwhile, a newer JPL library might declare the “f” field of the “C” class in a generic fashion, such as:
class C {                List<String>f;        
}
In the above example, even though “f” is declared to be of generic type “List<String>,” the string representation of “f” that is returned by the current specialized implementation of “toString( )” does not contain any generic type-specific information about “List<String>”; for example, the string representation does not say anything about the “List” type having a formal type parameter “<String>.” Because it might have been created before the introduction of generic types into the JPL, the program containing the “c.getClass( ).getField(“f”).toString( )” code might malfunction if the string representation returned by “toString( )” contained any such generic type-specific information. At the time the program was created, class “C” might have declared field “f” to be of a non-generic “List” type, so the program might not expect the string representation to contain such information.
Thus, there is not currently any method in the JPL Core Reflection API that can generate a string representation that indicates generic type-specific information about a reflective proxy that corresponds to a program element that is of a generic type or that contains a field that is of a generic type. Furthermore, altering “toString( )” could cause some existing programs to behave erratically.